/** @file

  Copyright (C) 2022 - 2023, Phytium Technology Co., Ltd. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Uefi.h>
#include <Library/ArmPlatformLib.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/PcdLib.h>
#include <Library/IoLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/ArmSmcLib.h>
#include <Library/I2CLib.h>
#include "Mhu.h"
#include "Scpi.h"

#define USING_FIXED_SENDER_ID

#ifndef USING_FIXED_SENDER_ID
STATIC INT32 GSenderId = 0;
#endif

volatile INT32 MhuDone = 0;
volatile INT32 MhuOsDone = 0;
volatile INT32 SensorCfg = 0;

STATIC VOID PrintHeader(SCPI_CMD_T *Header, INT32 Type)
{
    if(0 == Type)
        printf("SCPI requeset AP -> SCP : \n");
    else
        printf("SCPI Response SCP -> AP : \n");

    printf("Header : \n");
    printf("    Id     : 0x%x\n", Header->Id);
    printf("    Set    : 0x%x\n", Header->Set);
    printf("    Sender : 0x%x\n", Header->Sender);
    printf("    Size   : 0x%x\n", Header->Size);
    printf("    Status : 0x%x\n", Header->Status);
}

STATIC INT32 GetSenderId(VOID)
{
#ifndef USING_FIXED_SENDER_ID
    GSenderId++;
    GSenderId = 0xFF & GSenderId;
    return GSenderId;
#else
    return 0;
#endif
}

/*
*   CmdType: 0:standard 1:extend
*   Id: comand Id
*   SenderId: default 0
*   PlSize:Payload Size
*/
VOID InitMsg(SCPI_CMD_T * Header, INT32 CmdType, enum SCPI_CMD_ID Id, INT32 SenderId, INT32 PlSize)
{

    //Header = (SCPI_CMD_T *)malloc(sizeof(SCPI_CMD_T) + 512);
    /*Status = gBS->AllocatePool(AllocateAddress, sizeof(SCPI_CMD_T) + 512, (VOID **)&Header);*/

    /*if(EFI_ERROR(Status)) {*/
      /*DEBUG((EFI_D_ERROR, "%a() L%d : %r\n",__FUNCTION__, __LINE__, Status));*/
    /*}*/
    /*if(NULL == Header)*/
      /*return NULL;*/
    //memset(Header, 0, sizeof(SCPI_CMD_T) + 512);
    SetMem32(Header, sizeof(SCPI_CMD_T) + 512,0);
    Id = Id & 0x7f;
    Header->Id = Id;
    Header->Sender = SenderId;
    Header->Set = CmdType;
    Header->Size = PlSize;
}

STATIC UINTN GetEmptyPayload(UINTN Header)
{
    return Header + sizeof(SCPI_CMD_T);
}

/*STATIC VOID release_header(SCPI_CMD_T * Header)*/
/*{*/
    /*if(NULL != Header)*/
        /*gBS->FreePool(Header);*/
/*}*/

STATIC VOID* Mhu(INT32 Method, UINTN HeaderAddr, UINTN PlAddr, UINT32 CmdId)
{
    INT32  Res = 0;
    UINT32 PlSize = 0;
    SCPI_CMD_T* Header;
    UINTN ShareMemBase = 0;
    UINT8 Id;

    if(1 == Method ) {

        Res = MhuChnMessageStart(MHU_UBOOT_SCP_CHN, 0x0);
        if(SCP_OK != Res)
            return NULL;

        CopyMem((VOID *)AP_TO_SCP_SHARED_MEM_BASE, (VOID *)HeaderAddr, sizeof(SCPI_CMD_T));
        PlSize = ((SCPI_CMD_T *)HeaderAddr)->Size;
        if((0 != PlSize) && (0x0 != PlAddr))
            CopyMem((VOID *)(AP_TO_SCP_SHARED_MEM_BASE + sizeof(SCPI_CMD_T)),
                (VOID*)PlAddr, PlSize);


        Res = MhuChnMessageSend(MHU_UBOOT_SCP_CHN, 0x0);
        if(0 != Res)
            return NULL;

        Res = MhuChnMessageWait(MHU_SCP_UBOOT_CHN, 0x0);
        if(0 != Res)
            return NULL;


        Header = (SCPI_CMD_T *)SCP_TO_AP_SHARED_MEM_BASE;
        Id = Header->Id | (Header->Set << 7);
        if((CmdId != Id)) {
            printf("error! expect Id = 0x%x, but received Id = 0x%x \n",
                CmdId, Id);
                return NULL;
        }
        PrintHeader(Header, SCPI_HEAD_TYPE_RSP);

        MhuChnMessageEnd(MHU_SCP_UBOOT_CHN, 0x0);

        ShareMemBase = SCP_TO_AP_SHARED_MEM_BASE;

        //return (VOID *)(SCP_TO_AP_SHARED_MEM_BASE + sizeof(SCPI_CMD_T));

    } else if(2 == Method){

        Res = MhuChnMessageStart(MHU_OS_SCP_CHN, 0x0);
        if(SCP_OK != Res)
            return NULL;

        CopyMem((VOID *)AP_TO_SCP_OS_MEM_BASE, (VOID *)HeaderAddr, sizeof(SCPI_CMD_T));
        PlSize = ((SCPI_CMD_T *)HeaderAddr)->Size;
        if((0 != PlSize) && (0x0 != PlAddr))
            CopyMem((VOID *)(AP_TO_SCP_OS_MEM_BASE + sizeof(SCPI_CMD_T)),
                (VOID*)PlAddr, PlSize);

        Res = MhuChnMessageSend(MHU_OS_SCP_CHN, 0x0);
        if(0 != Res)
            return NULL;

        Res = MhuChnMessageWait(MHU_SCP_OS_CHN, 0x0);
        if(0 != Res)
            return NULL;


        Header = (SCPI_CMD_T *)SCP_TO_AP_OS_MEM_BASE;
        if((CmdId != Header->Id)) {
            printf("error! expect Id = 0x%x, but received Id = 0x%x \n",
                CmdId, Header->Id);
                return NULL;
        }
        PrintHeader(Header, SCPI_HEAD_TYPE_RSP);

        MhuChnMessageEnd(MHU_SCP_OS_CHN, 0x0);

        ShareMemBase = SCP_TO_AP_OS_MEM_BASE;

        //return (VOID *)(SCP_TO_AP_OS_MEM_BASE + sizeof(SCPI_CMD_T));
    }
        return (VOID *)(ShareMemBase + sizeof(SCPI_CMD_T));

}

INT32 FuncGetScpCapability(INT32 Method)
{
    //struct ARM_SMC_ARGS smc_res;
    /*SCPI_CMD_T *Header = (SCPI_CMD_T *)(sizeof(SCPI_CMD_T) + 512);*/
    UINT8 Header[sizeof(SCPI_CMD_T) + 512];
    //Header = (SCPI_CMD_T *)malloc(sizeof(SCPI_CMD_T) + 512);
    INT32 SenderId = GetSenderId();
    INT32 Res = 0;
    PL_SCP_CAPABILITY_T *PlRsp = NULL;

    InitMsg((SCPI_CMD_T *)Header, SCPI_SET_NORMAL, CMD_ID_GET_SCP_CAPABILITY, SenderId, 0);
    if(NULL == Header)
        return SCP_E_NOMEM;


    PrintHeader((SCPI_CMD_T *)Header, SCPI_HEAD_TYPE_REQ);
    if(0 == Method) {

        //arm_smccc_smc(SMC_SCPI_ID, (unsigned long)Header, 0, 0, 0, 0, 0, 0, &smc_res);
        //PlRsp = (PL_SCP_CAPABILITY_T *)extract_rsp_payload(&smc_res, CMD_ID_GET_SCP_CAPABILITY, SenderId);
    } else {
        PlRsp = (PL_SCP_CAPABILITY_T *)Mhu(Method, (UINTN)Header, 0, CMD_ID_GET_SCP_CAPABILITY);
    }

    if(NULL == PlRsp) {
        printf("get rsp Payload failed.\n");
        Res = 1;
        goto failed;
    }

    printf("Payload : \n");
    printf("    Version : 0x%x\n", PlRsp->Version);
    printf("    limites : 0x%x\n", PlRsp->Limits);
    printf("    firmware Version : 0x%x\n", PlRsp->FirmwareVersion);
    printf("    command enable 0 : 0x%x\n", PlRsp->CmdEnable0);
    printf("    command enable 1 : 0x%x\n", PlRsp->CmdEnable1);
    printf("    command enable 2 : 0x%x\n", PlRsp->CmdEnable2);
    printf("    command enable 3 : 0x%x\n", PlRsp->CmdEnable3);


failed:
    /*release_header(Header);*/
    return Res;
}

#if 1
//{  500, 0x3e800001 , 0x14001400 , 0x14001400 },
INT32 FuncSetFreqWithTemp(INT32 Method)
{
    UINT8 Header[sizeof(SCPI_CMD_T) + 512];
    INT32 SenderId = GetSenderId();
    INT32 Res = 0;
    ST_TEMP_T StTemp = {
        .HighTemp = 80,
        .LowTemp = 70,
        .Pll0 = 0x3e800001,
        .Pll1 = 0x14001400,
        .Pll2 = 0x14001400,
        .Time = 2
    };

    InitMsg((SCPI_CMD_T *)Header, SCPI_SET_EXTENDED, CMD_ID_EX_SET_FREQ, SenderId, sizeof(ST_TEMP_T));
    if(NULL == Header)
        return SCP_E_NOMEM;

    ST_TEMP_T *Payload = (ST_TEMP_T *)GetEmptyPayload((UINTN)Header);

    CopyMem(Payload, &StTemp, sizeof(ST_TEMP_T));
    PrintHeader((SCPI_CMD_T *)Header, SCPI_HEAD_TYPE_REQ);
    printf("Payload : \n");
    printf("    HighTemp: %d\n", Payload->HighTemp);
    printf("    low_tmep : %d\n", Payload->LowTemp);
    printf("    Pll0 : 0x%x\n", Payload->Pll0);
    printf("    Pll1 : 0x%x\n", Payload->Pll1);
    printf("    Pll2 : 0x%x\n", Payload->Pll2);

    if(0 == Method) {
        //arm_smccc_smc(SMC_SCPI_ID, (unsigned long)Header,
        //    (unsigned long)Payload, 0, 0, 0, 0, 0, &smc_res);
        //PlRsp = (PL_GET_SENSOR_VALUE_RSP_T *)
        //    extract_rsp_payload(&smc_res, CMD_ID_GET_SENSOR_VALUE, SenderId);
    } else {
        Mhu(Method, (UINTN)Header, (unsigned long)Payload, CMD_ID_EX_SET_FREQ);
    }

    return Res;
}

INT32 FuncSetI2cPort(INT32 Method)
{
    UINT8 Header[sizeof(SCPI_CMD_T) + 512];
    INT32 SenderId = GetSenderId();
    INT32 Res = 0;
    ST_I2C_PORT_T StI2cPort = {
        .I2cPort = 3,
    };
    enable_i2c_as_slaver(0x28009000, 0x23);
    InitMsg((SCPI_CMD_T *)Header, SCPI_SET_EXTENDED, CMD_ID_EX_SET_I2C_PORT, SenderId, sizeof(ST_I2C_PORT_T));
    if(NULL == Header)
        return SCP_E_NOMEM;

    ST_I2C_PORT_T *Payload = (ST_I2C_PORT_T *)GetEmptyPayload((UINTN)Header);

    CopyMem(Payload, &StI2cPort, sizeof(ST_I2C_PORT_T));
    PrintHeader((SCPI_CMD_T *)Header, SCPI_HEAD_TYPE_REQ);
    printf("Payload : \n");
    printf("    I2cPort : %d\n", Payload->I2cPort);

    if(0 == Method) {
        //arm_smccc_smc(SMC_SCPI_ID, (unsigned long)Header,
        //    (unsigned long)Payload, 0, 0, 0, 0, 0, &smc_res);
        //PlRsp = (PL_GET_SENSOR_VALUE_RSP_T *)
        //    extract_rsp_payload(&smc_res, CMD_ID_GET_SENSOR_VALUE, SenderId);
    } else {
        Mhu(Method, (UINTN)Header, (unsigned long)Payload, CMD_ID_EX_SET_I2C_PORT);
    }

    return Res;
}


INT32 FuncSetVotage(INT32 Method)
{
    UINT8 Header[sizeof(SCPI_CMD_T) + 512];
    INT32 SenderId = GetSenderId();
    INT32 Res = 0;
    ST_VOLTAGE_T st_voltage = {
        .Mode = 1,
        .I2cPort = 3,
        .UcDevAddr = 0x60,
        .UcWregAddr = 0x21,
        .UcRregAddr = 0x8b,
        .AusData[0] = 720,
        .AusData[1] = 760,
        .AusData[2] = 760,
        .AusData[3] = 800,
    };

    InitMsg((SCPI_CMD_T *)Header, SCPI_SET_EXTENDED, CMD_ID_EX_SET_VOLTAGE, SenderId, sizeof(ST_VOLTAGE_T));
    if(NULL == Header)
        return SCP_E_NOMEM;

    ST_VOLTAGE_T *Payload = (ST_VOLTAGE_T *)GetEmptyPayload((UINTN)Header);

    CopyMem(Payload, &st_voltage, sizeof(ST_VOLTAGE_T));
    PrintHeader((SCPI_CMD_T *)Header, SCPI_HEAD_TYPE_REQ);
    printf("Payload : \n");
    printf("    Mode: %d\n", Payload->Mode);
    printf("    I2cPort : %d\n", Payload->I2cPort);
    printf("    UcDevAddr : %d\n", Payload->UcDevAddr);
    printf("    UcWregAddr : %d\n", Payload->UcWregAddr);
    printf("    UcRregAddr : %d\n", Payload->UcRregAddr);
    printf("    AusData[0] : %d\n", Payload->AusData[0]);
    printf("    AusData[1] : %d\n", Payload->AusData[1]);
    printf("    AusData[2] : %d\n", Payload->AusData[2]);
    printf("    AusData[3] : %d\n", Payload->AusData[3]);

    if(0 == Method) {
        //arm_smccc_smc(SMC_SCPI_ID, (unsigned long)Header,
        //    (unsigned long)Payload, 0, 0, 0, 0, 0, &smc_res);
        //PlRsp = (PL_GET_SENSOR_VALUE_RSP_T *)
        //    extract_rsp_payload(&smc_res, CMD_ID_GET_SENSOR_VALUE, SenderId);
    } else {
        Mhu(Method, (UINTN)Header, (unsigned long)Payload, CMD_ID_EX_SET_VOLTAGE);
    }

    return Res;
}

INT32 FuncGetSensorValue(INT32 Method, INT32 SensorId)
{
    //struct arm_smccc_res smc_res;
    UINT8 Header[sizeof(SCPI_CMD_T) + 512];
    INT32 SenderId = GetSenderId();
    INT32 Res = 0;
    PL_GET_SENSOR_VALUE_RSP_T *PlRsp = NULL;

    //if(1 != (argc - args_indx))
    //    return CMD_RET_USAGE;

    InitMsg((SCPI_CMD_T *)Header, SCPI_SET_NORMAL, CMD_ID_GET_SENSOR_VALUE, SenderId, sizeof(PL_GET_SENSOR_VALUE_REQ_T));
    if(NULL == Header)
        return SCP_E_NOMEM;

    PL_GET_SENSOR_VALUE_REQ_T *Payload = (PL_GET_SENSOR_VALUE_REQ_T *)GetEmptyPayload((UINTN)Header);
    //Payload->SensorId = simple_strtoul(argv[args_indx], NULL, 16);
    Payload->SensorId = SensorId;

    PrintHeader((SCPI_CMD_T *)Header, SCPI_HEAD_TYPE_REQ);
    printf("Payload : \n");
    printf("    sensor Id : 0x%x\n", Payload->SensorId);

    if(0 == Method) {
        //arm_smccc_smc(SMC_SCPI_ID, (unsigned long)Header,
        //    (unsigned long)Payload, 0, 0, 0, 0, 0, &smc_res);
        //PlRsp = (PL_GET_SENSOR_VALUE_RSP_T *)
        //    extract_rsp_payload(&smc_res, CMD_ID_GET_SENSOR_VALUE, SenderId);
    } else {
        PlRsp = (PL_GET_SENSOR_VALUE_RSP_T *)
            Mhu(Method, (UINTN)Header, (unsigned long)Payload, CMD_ID_GET_SENSOR_VALUE);
    }

    if(NULL == PlRsp) {
        printf("get rsp Payload failed.\n");
        Res = 1;
        goto failed;
    }
    printf("Payload : \n");
    printf("    value low  : 0x%x\n", PlRsp->ValueLow);
    printf("    value high : 0x%x\n", PlRsp->ValueHigh);

failed:
    return Res;
}
#endif

EFI_STATUS
ScpLibInit (
  VOID
  )
{

  MhuChnInit(MHU_UBOOT_SCP_CHN);
  MhuChnInit(MHU_SCP_UBOOT_CHN);

  return EFI_SUCCESS;
}
#if 0
EFI_STATUS
EFIAPI
InitializeScpiDxe (
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable
)

{
    MhuChnInit(MHU_UBOOT_SCP_CHN);
    MhuChnInit(MHU_SCP_UBOOT_CHN);
    FuncGetScpCapability(1);
    /*FuncGetSensorValue(1, 0);*/
    /*FuncSetFreqWithTemp(1);*/
    /*FuncSetVotage(1);*/
    /*FuncSetI2cPort(1);*/

    return 0;
}
#endif
